﻿using System;
using System.Collections;
using System.Configuration;
using System.IO;
using System.Text;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;
using ContainerVariations.Common;
using NUnit.Framework;
using Configuration=Castle.Windsor.Installer.Configuration;

namespace ContainerVariations.CastleSamples
{
    [TestFixture]
    public class CastleFixture : TemplateFixture
    {
        [Test]
        public override void Configure_a_single_object_with_container_lifetime()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(Component.For<IKnob>().ImplementedBy<Knob>());

                var component01 = container.Resolve<IKnob>();
                var component02 = container.Resolve<IKnob>();

                Assert.That(component01, Is.Not.Null);
                Assert.That(component01, Is.TypeOf(typeof(Knob)));
                Assert.That(component01, Is.EqualTo(component02));
            }
        }

        [Test]
        public override void Configure_a_single_object_with_transient_lifetime()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(Component.For<IKnob>().ImplementedBy<Knob>().LifeStyle.Transient);

                var component01 = container.Resolve<IKnob>();
                var component02 = container.Resolve<IKnob>();

                Assert.That(component01, Is.Not.Null);
                Assert.That(component01, Is.TypeOf(typeof(Knob)));
                Assert.That(component01, Is.Not.EqualTo(component02));
            }
        }

        [Test]
        public override void Configure_an_autowired_dependency_graph()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(
                    Component.For<IKnob>().ImplementedBy<Knob>(),
                    Component.For<IDoor>().ImplementedBy<Door>(),
                    Component.For<IHouse>().ImplementedBy<House>()
                    );

                var house = container.Resolve<IHouse>();

                Assert.That(house, Is.Not.Null);
                house.OpenDoor();
            }
        }

        [Test]
        public override void Configure_a_manually_wired_dependency_graph()
        {
            // Not sure if it is possible with dynamic registration.
        }

        [Test]
        public override void Xml_configure_an_autowired_dependency_graph()
        {
            using (IWindsorContainer container = new WindsorContainer(new XmlInterpreter("configuration.xml")))
            {
                var house = container.Resolve<IHouse>();

                Assert.That(house, Is.Not.Null);
                house.OpenDoor();
            }
        }

        [Test]
        public override void Configure_container_dynamically_supplemented_with_xml()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(Component.For<Apple>().ImplementedBy<Apple>());
                container.Register(Component.For<IAppleContents>().ImplementedBy<AppleSeeds>());
                container.Install(Configuration.FromXmlFile("configuration.xml"));

                Assert.That(container.Resolve<Apple>(), Is.TypeOf(typeof(Apple)));
                Assert.That(container.Resolve<Orange>(), Is.TypeOf(typeof(Orange)));
            }
        }

        [Test]
        public override void Configure_container_dynamically_modified_by_xml()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                // The first component registered is the default!
                container.Install(Configuration.FromXmlFile("configuration.xml"));

                container.Register(
                    Component.For<Apple>()
                    .ImplementedBy<Apple>());
                // This registration (AppleSeeds) should be replaced by the Worm
                //   component registration in the app.config file because the
                //   app.config registrations are applied first, and the first
                //   registered is the default.
                container.Register(
                    Component.For<IAppleContents>()
                    .ImplementedBy<AppleSeeds>());

                var apple = container.Resolve<Apple>();
                Assert.That(apple.ToString(), Is.EqualTo("Apple contains: Worm"));
            }
        }

        [Test]
        public override void Configure_an_open_generic()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(
                    Component.For(typeof(IFlattener<>))
                        .ImplementedBy(typeof(PassthruFlattener<>)),
                    Component.For(typeof(IFlattener<Contact>))
                        .ImplementedBy<ContactFlattener>()
                    );

                var contactFlattener = container.Resolve<IFlattener<Contact>>();
                var addressFlattener = container.Resolve<IFlattener<Address>>();
                Assert.That(contactFlattener, Is.TypeOf(typeof(ContactFlattener)));
                Assert.That(addressFlattener, Is.TypeOf(typeof(PassthruFlattener<Address>)));
            }
        }

        [Test]
        public override void Configure_a_type_with_a_lambda_or_delegate_dependency()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                const string expectedValue = "This is the value we're assigning.";
                string actualValue = null;

                Action<string> action = x => actualValue = x;

                container.Register(
                    Component.For<ITestActor>()
                        .ImplementedBy<TestActor>()
                        .DependsOn(
                        Property.ForKey("action").Eq(action)));

                var actor = container.Resolve<ITestActor>();
                actor.Act(expectedValue);

                Assert.That(actualValue, Is.EqualTo(expectedValue));
            }
        }

        [Test]
        public override void Configure_all_classes_of_a_specified_type_in_a_specified_assembly()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(
                    AllTypes
                        .Of<IWidget>()
                        .FromAssembly(typeof (IWidget).Assembly));

                IWidget[] widgets = container.ResolveAll<IWidget>();

                foreach (var widget in widgets)
                {
                    Console.WriteLine(widget.GetType().Name + ": " + widget.Call());
                }

                Assert.That(widgets.Length, Is.EqualTo(4));
            }
        }

        [Test]
        public override void Configure_a_class_with_multiple_decorators()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(
                    Component
                        .For<IDoorUser>()
                        .ImplementedBy<DoorUser>()
                        .DependsOn(Property.ForKey("mayOpenDoor").Eq(true)),
                    Component
                        .For<IKnob>()
                        .ImplementedBy<Knob>(),
                    Component
                        .For<IDoor>()
                        .ImplementedBy<EmailDoor>()
                        .Named("email-door")
                        .ServiceOverrides(ServiceOverride.ForKey("door").Eq("secure-door")),
                    Component
                        .For<IDoor>()
                        .ImplementedBy<Door>()
                        .Named("base-door"),
                    Component
                        .For<IDoor>()
                        .ImplementedBy<SecureDoor>()
                        .Named("secure-door")
                        .ServiceOverrides(ServiceOverride.ForKey("door").Eq("base-door"))
                    );

                var door = container.Resolve<IDoor>();
                door.Open();
            }
        }

        [Test]
        public override void Configure_a_class_with_scalar_dependencies_from_configuration_api()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                bool mayOpenDoor = Convert.ToBoolean(ConfigurationManager.AppSettings["mayOpenDoor"]);
                container.Register(
                    Component
                        .For<IDoorUser>()
                        .ImplementedBy<DoorUser>()
                        .DependsOn(Property.ForKey("mayOpenDoor").Eq(mayOpenDoor)));
                var doorUser = container.Resolve<IDoorUser>();
                Assert.That(doorUser.MayOpenDoor, Is.EqualTo(mayOpenDoor));
            }
        }

        [Test]
        public override void Container_can_use_an_instance_created_outside_the_container()
        {
            var stringBuilder = new StringBuilder();
            var writer = new StringWriter(stringBuilder);
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(Component.For<TextWriter>().Instance(writer));
                container.Register(Component.For<ISender>().ImplementedBy<TextWriterSender>());

                var sender = container.Resolve<ISender>();

                const string message = "This is the message to send.";
                sender.Send(message);
                Assert.That(stringBuilder.ToString(), Is.EqualTo(message));
            }
        }

        [Test]
        public override void Disposable_classes_with_container_lifetime_are_disposed_when_container_is_disposed()
        {
            IKnob knob;
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(Component.For<IKnob>().ImplementedBy<Knob>());
                knob = container.Resolve<IKnob>();
                Assert.That(knob.IsDisposed, Is.False);
            }
            Assert.That(knob.IsDisposed, Is.True);
        }

        [Test]
        public override void Disposable_classes_with_transient_lifetime_are_disposed_when_container_is_disposed()
        {
            IKnob knob;
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(Component.For<IKnob>().ImplementedBy<Knob>().LifeStyle.Transient);
                knob = container.Resolve<IKnob>();
                Assert.That(knob.IsDisposed, Is.False);
            }
            Assert.That(knob.IsDisposed, Is.True);
        }

        [Test]
        public override void Register_components_with_scalar_dependencies()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(
                    Component.For<Answer>()
                    .Parameters(Parameter.ForKey("value").Eq("42"))
                    );

                Assert.That(container.Resolve<Answer>().Value, Is.EqualTo(42));
            }
        }

        [Test]
        public override void Resolve_components_providing_scalar_dependencies()
        {
            using (IWindsorContainer container = new WindsorContainer())
            {
                container.Register(
                    Component.For<Answer>()
                    );

                var args = new Hashtable();
                args["value"] = 42;
                var answer = container.Resolve<Answer>(args);
                Assert.That(answer.Value, Is.EqualTo(42));
            }
        }
    }
}